//-------------------------------------------------------------------------
//
//  Copyright (c) 1999 Cornell University
//  Computer Systems Laboratory
//  Cornell University, Ithaca, NY 14853
//  All Rights Reserved
//
//  Permission to use, copy, modify, and distribute this software
//  and its documentation for any purpose and without fee is hereby
//  granted, provided that the above copyright notice appear in all
//  copies. Cornell University makes no representations
//  about the suitability of this software for any purpose. It is
//  provided "as is" without express or implied warranty. Export of this
//  software outside of the United States of America may require an
//  export license.
//
//  $Id: cpu.v,v 1.1.1.1 2003/01/16 19:49:43 heinrich Exp $
//
//-------------------------------------------------------------------------

`include "mips.h"

module	cpu
(
	CLK,				// Clock signal
	Daddr, Dread, Dwrite, Dout, Dsize,
	Din, Iaddr, Iin,		// Memory interface
        MRST				// Master reset
);

//--------------------------------------------------------------------

input		CLK;			// Clock 
input	[31:0]	Din;			// Read path to data cache
input	[31:0]	Iin;			// Instruction input
input		MRST;			// Master reset

output  [31:0]	Daddr;			// Address for data access
output	[31:0]	Dout;			// Write path to data cache
output		Dread;			// Data read
output		Dwrite;			// Data write enable
output	[1:0]	Dsize;
output	[31:0]	Iaddr;			// Address for instruction access

/*--------------------------------------------------------------------
Global Signals
---------------------------------------------------------------------*/
// IF
reg	[31:0] 	PC, savedBranchPC;	// PC chain
reg	[31:0]  IR1, IR2, IR3, IR4; 	// IR chain
wire	[4:0]  	PCsel;			// Sel control for program counter
wire	[31:0] 	PCoffset;		// Offset value for branches
wire	[25:0] 	PCjump;			// Destination address for jumps
wire 	[31:0] 	PCbr, PCinc, PCjmp;
reg 	[2:0] 	State;
reg	    [2:0]   NextState;
reg		inDelaySlot;

// RD
wire 	[4:0]	RSaddr;			// Source reg address specifier
wire 	[4:0]	RTaddr;			// Target reg address specifier
wire 	[31:0]	Imm;			// Immediate data
reg	[31:0] 	ImmBus;			// Immediate bus
wire	[31:0]	RSbus; 			// S source bus
wire	[31:0] 	RTbus;			// T source bus
wire	[31:0] 	RDbus;			// Destination bus
wire		instIsSyscall;		// syscall inst in decode
reg		rInstIsSyscall;		// one cycle later
reg		rrInstIsSyscall;	// one cycle later
wire		decodeStall;		// should I stall the pipe?

// EX
wire  	[31:0] 	ALUout;			// pre ALU output register
wire	       	QCResult;		// Result from quick compare logic
wire	[5:0]  	QCsel;
wire 	[7:0]  	ALUsel;
wire	       	UseImm;
wire		instIsBranch;

// MEM
reg 	[31:0] 	MAR, LMDR, SMDR;	// Mem address and data registers
reg	    [31:0]  loadData;
reg   	[31:0]  preLoadData;

// WB
reg   	[31:0]	WBreg;			// write back register
wire     [4:0]	RDaddr;			// Destination register address
wire     [2:0]	WBsel;			// Stage 5 result select
wire           	WriteCLK;              	// Write clock	

   reg [31:0] 	lastPC;
   
initial  begin
//   $monitor("%d: MRST = %b\n", $time, MRST);
//   $monitor("time=%d, pc=%h, RTaddr=%h, state=%h, RSbus=%h, RTbus=%h\n", $time, PC, RTaddr, State, RSbus, RTbus);
//   $monitor("time = %d, WBsel = %h, PCinc = %h, WBreg = %h\n", $time,
//      WBsel, PCinc, WBreg);
//   $monitor("%d:\n ###########  WriteClk = %b #############\n\n", $time,
//      WriteCLK);
//   $monitor("%d: IR4 op = %h, dest1=%h, dest2=%h\n", $time, IR4[`op], IR4[`rt], IR4[`rd]);
//   $monitor("%d: State = %d\n", $time, State);
//   $monitor("%d: NextState = %d\n", $time, NextState);

end 

reg	[31:0]	num_instructions;

// -------------------------------------------------------------------
// This stage is the state machine for the multicycle MIPS only
// -------------------------------------------------------------------

// State Machine to cycle through the States
always @(State or decodeStall) begin
   case (State) 
      `IF_STATE: begin
	            NextState = `ID_STATE;
                 end

      `ID_STATE: begin
		    if (!decodeStall) begin
  	               NextState = `EX_STATE;
                    end
	            else begin
  	               NextState = `ID_STATE;
		    end
                 end

      `EX_STATE: begin
	            NextState = `MEM_STATE;
                 end

      `MEM_STATE: begin
	            NextState = `WB_STATE;
                  end

      `WB_STATE: begin
	            NextState = `IF_STATE;
	            num_instructions = num_instructions + 1;
                 end
      endcase
end

// FLOPs that reset the machine to a usable state, and then flow through 
// the pipe.  The instruction register is flopped for each pipeline stage
// and any control signals for that stage should come from that stages
// version of the instruction register.

always @(posedge CLK) begin
   if (MRST) begin
      num_instructions <= `TICK 32'b0;
      IR1 <= `TICK 32'b0;
      IR2 <= `TICK 32'b0;
      IR3 <= `TICK 32'b0;
      IR4 <= `TICK 32'b0;
      State <= `TICK `IF_STATE;
   end
   else begin
      IR1 <= `TICK Iin;
      IR2 <= `TICK IR1;
      IR3 <= `TICK IR2;
      IR4 <= `TICK IR3;
      State <= `TICK NextState;
   end

   if ((State == `EX_STATE) && (inDelaySlot) && (instIsBranch)) begin
      $display("Branches are not allowed in delay slots!\n");
      $display("PC is %h, inst is:\n", PC);
      $disasm(IR2);
      $finish;
   end
end

/*--------------------------------------------------------------------
Instruction Fetch Stage
---------------------------------------------------------------------*/

ifcontr IFcontr 			// IF control logic
(
	.I2 (IR2),
	.QC (QCResult),
	.PCsel (PCsel), 
	.PCoffset (PCoffset), 
	.PCjump (PCjump)
); 						


/*--------------------------------------------------------------------
PCsection: Update program counter and keep PC and Instruction register
           chains.  In the pipelined MIPS, the PC is updated in the
           EX phase.
---------------------------------------------------------------------*/

// Virtual instruction address taken from PC
assign Iaddr = PC;	
assign PCbr  = PC + 4 + PCoffset;
assign PCinc = PC + 32'h00000004;
assign PCjmp = {PC[31:28],PCjump,2'b0};

assign instIsBranch = ~(PCsel == `select_pc_inc);

// PC is now flopped on negedge CLK in an attempt to emulate the way
// the MIPS R3000 shifts the IF stage by half a phase.  In the pipelined
// model you can think of this like the PC gets flopped half-way through
// the EX phase.  The value will either be the PC+4 or the branch target.

// The logic dealing with "inDelaySlot" and "savedBranchPC" is needed only
// for the multi-cycle model, and can be deleted (along with this comment)
// for the pipelined model.  PC is also updated every cycle not just in the
// IF_STATE (which of course goes away also).

always @(negedge CLK) begin
   if (MRST) begin
      PC <= `TICK 32'hbfc00000;				// Reset vector
      inDelaySlot <= `TICK 1'b0;
   end
   else begin
      if (State == `IF_STATE) begin
	 // select the new PC
	 if (inDelaySlot) begin
	    PC <= `TICK savedBranchPC;
	 end
	 else begin
	    PC <= `TICK PCinc;

	    case(PCsel)		     
	       `select_pc_add:	    savedBranchPC <= `TICK PCbr;
	       `select_pc_jump:     savedBranchPC <= `TICK PCjmp;
	       `select_pc_register: savedBranchPC <= `TICK RSbus;
	    endcase 
	 end

	 // Here "inst" refers to the previous instruction
	 inDelaySlot <= `TICK instIsBranch;
      end
   end
end

/*--------------------------------------------------------------------
RF read stage  (RD) -- Read operands from register file
---------------------------------------------------------------------*/

rf regfile		// Register File
(	
	.WCLK	(WriteCLK),
	.RSaddr	(RSaddr), 
	.RTaddr	(RTaddr), 
	.RDaddr	(RDaddr), 
	.RS	(RSbus), 
	.RT	(RTbus),
	.RD	(RDbus),
	.UseImm	(UseImm)
);

rd RDcontr		// Register File and immediate control logic
(
	.I1	(IR1),
     	.RSaddr	(RSaddr),
     	.RTaddr	(RTaddr),
     	.Imm	(Imm),
	.instIsSyscall (instIsSyscall)
);

// Register File Write Clock
assign WriteCLK = (State == `WB_STATE) & (CLK == 1'b0);

// Flop the Imm field from Decode for use in the EX stage
always @(posedge CLK) begin
   ImmBus <= `TICK Imm;
end

// -------------------------------------------------------------------
// This section is code that detects syscall instructions and stalls
// them in the Decode stage for 2 cycles.  This will become necessary
// in our pipelined model so that our syscall emulation routines will
// function properly.  Do not change this code unless you have a 
// *really* good reason
// -------------------------------------------------------------------

always @(posedge CLK) begin
   rInstIsSyscall <= `TICK instIsSyscall;
   rrInstIsSyscall <= `TICK rInstIsSyscall;
end

assign decodeStall = (instIsSyscall | rInstIsSyscall) & !rrInstIsSyscall;

// -------------------------------------------------------------------

/*--------------------------------------------------------------------
Execute Stage (EX) -- Perform ALU or SHIFT Operation
---------------------------------------------------------------------*/

ex EXcontr		// ALU control
(
	.I2		(IR2),
        .QCsel		(QCsel),
        .ALUsel		(ALUsel),
        .UseImm		(UseImm)
);

alu	ALU	
(
	.RSbus	(RSbus),
	.RTbus	(RTbus),
	.Imm	(ImmBus),
	.UseImm	(UseImm),
	.SEL	(ALUsel),
        .ALUout (ALUout)
);

// Quick compare logic
qc	QC	
(
	.RSbus	(RSbus),
	.RTbus	(RTbus),
	.QCsel	(QCsel),
        .Result (QCResult)
);

/*--------------------------------------------------------------------
Memory Stage  (MEM) -- Access Data Memory
---------------------------------------------------------------------*/

mem MEMcontr				// Memory access control
(
	.I3	(IR3),
        .Dread  (Dread),
     	.Dwrite (Dwrite),
  	.Dsize  (Dsize),
        .State  (State)
);

// Handle sub-word loads (ugh)
always @(Din or IR3 or Daddr) begin
   if (IR3[`op] == `LBU) begin
      loadData = (Din >> ((~Daddr & 32'h3) << 3)) & 32'hff;
   end
   else if (IR3[`op] == `LB) begin
      preLoadData = (Din >> ((~Daddr & 32'h3) << 3)) & 32'hff;
      // sign extend!
      loadData = {{24{preLoadData[7]}}, preLoadData[7:0]};
   end
   else if (IR3[`op] == `LHU) begin
   	loadData = (Din >> (((~Daddr >> 1) & 32'h1) << 4)) & 32'hffff;
   end
   else if (IR3[`op] == `LH) begin
   	preLoadData = (Din >> (((~Daddr >> 1) & 32'h1) << 4)) & 32'hffff;
   	// sign extend!
   	loadData = {{16{preLoadData[15]}}, preLoadData[15:0]};
   end
   else begin
      loadData = Din;
   end
   //$display("%d:LoadData= %d\n", $time, loadData);
end

always @(posedge CLK) begin 
   SMDR <= `TICK RTbus;
   MAR  <= `TICK ALUout;
   LMDR <= `TICK loadData;
end

assign Daddr = MAR;			// Data memory address
assign Dout = SMDR;			// Data memory data out

/*--------------------------------------------------------------------
 Writeback Stage (WB) -- Write data back to register file
---------------------------------------------------------------------*/

wb WBcontr			// Register write back control
(
	.I4	(IR4),
     	.RDaddr (RDaddr),
     	.WBsel	(WBsel)
);


assign RDbus = WBreg;		// Drive RDbus with contents of WBreg

always @(MAR or LMDR or PCinc or WBsel)			
begin		
   // Select write back value
   case (WBsel)
	`select_wb_load : WBreg = LMDR;
	`select_wb_link : WBreg = PCinc + 32'h4;
	default :  	  WBreg = MAR;
   endcase
end

endmodule		
